package com.ylr.admin.framework.service.impl;

import com.ylr.admin.common.constant.AdminConstant;
import com.ylr.admin.framework.service.DeptService;
import com.ylr.admin.framework.service.RoleUserService;
import com.ylr.admin.framework.service.UserService;
import com.ylr.base.common.constant.RedisConstant;
import com.ylr.base.common.dto.PageDto;
import com.ylr.base.common.enums.CommonEnum;
import com.ylr.base.common.utils.RandomNumUtils;
import com.ylr.base.common.utils.ThrottleUtils;
import com.ylr.base.framework.service.AsyncService;
import com.ylr.blog.framework.resolver.YbBlogSpaceResolver;
import com.ylr.config.common.enums.Enum;
import com.ylr.config.framework.resolver.YcEnumResolver;
import com.ylr.redis.framework.service.RedisService;
import com.ylr.redis.framework.service.RedisStringService;
import com.ylr.redis.framework.service.RedissonService;
import com.ylr.user.common.dto.user.UserDto;
import com.ylr.user.common.dto.user.UserEnumDto;
import com.ylr.user.common.enums.UserEnum;
import com.ylr.user.common.vo.user.UserFormVo;
import com.ylr.user.common.vo.user.UserVo;
import com.ylr.user.framework.resolver.YuUserResolver;
import lombok.AllArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * Created by IntelliJ IDEA.
 *
 * @author wyw
 * date: 2023-05-12 14:48:37
 * className: UserServiceImpl
 * version: 1.0
 * description:
 */
@Service("userService")
@AllArgsConstructor
public class UserServiceImpl implements UserService {

    private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class);

    private final RoleUserService roleUserService;

    private final DeptService deptService;

    private final YuUserResolver yuUserResolver;

    private final YcEnumResolver ycEnumResolver;

    private final YbBlogSpaceResolver ybBlogSpaceResolver;

    private final AsyncService asyncService;

    private final RedisStringService redisStringService;

    private final RedisService redisService;

    private final RedissonService redissonService;

    private final ThrottleUtils throttleUtils;

    @Transactional
    @Override
    public void saveUser(UserVo userVo) {
        /*
            TODO 方法需要分布式事务支持
         */

        // 1、保存用户
        Long userId = yuUserResolver.saveUser(userVo);

        /*
            2、用户关联默认角色
                2.1、查询枚举配置，获取用户默认关联的角色主键列表
                2.2、用户与角色进行关联
         */
        List<Long> roleIdList = ycEnumResolver.getValueList(Enum.DEFAULT_ROLE.getType());
        log.info("枚举配置的默认角色。roleIdList={}", roleIdList);
        if (!roleIdList.isEmpty()) {
            // 用户与角色进行关联
            roleUserService.addOrModifyRoleUser(roleIdList, userId);
        }

        // 3、调用blog模块，为用户生成一个默认空间
        ybBlogSpaceResolver.addBlogSpace(userId, AdminConstant.DEFAULT_BLOG_SPACE);

    }

    @Override
    public PageDto<UserDto> pageUserList(UserFormVo userFormVo) {
        // 分页查询用户
        PageDto<UserDto> page = yuUserResolver.pageUserList(userFormVo);

        List<UserDto> userList = page.getList();
        // 收集所有部门主键
        Set<Long> deptIdSet = userList.stream().map(UserDto::getDeptId).map(Long::parseLong).collect(Collectors.toSet());

        // 获取部门主键与部门名称的映射关系
        Map<String, String> deptNameMap = deptService.getDeptNameMap(deptIdSet);
        userList.forEach(user -> {
            // 设置部门名称
            user.setDeptName(deptNameMap.get(user.getDeptId()));
        });

        return page;
    }

    @Override
    public UserDto getUser(Long id) {
        return yuUserResolver.getUser(id);
    }

    @Override
    public UserEnumDto getPageEnums() {

        UserEnumDto userEnumDto = new UserEnumDto();
        userEnumDto.setLeaderEnumList(UserEnum.getLeaderEnumList())
                .setStatusEnumList(CommonEnum.getStatusEnumList());

        return userEnumDto;
    }

    @Override
    public List<UserDto> getRoleUserList(String roleKey) {

        String redisKey = AdminConstant.ROLE_KEY_PREFIX + roleKey;
        long randomNum = RandomNumUtils.randomNum(10, 300);

        List<UserDto> cacheUserList = redisStringService.get(redisKey);
        if (Objects.nonNull(cacheUserList)) {
            Runnable runnable = () -> {
                // 尝试更新缓存过期时间
                redisService.tryUpdateKeyExpire(redisKey, randomNum / 2, randomNum);
            };
            throttleUtils.throttleRun(runnable, "getRoleUserList", 1500, TimeUnit.MILLISECONDS);

            return cacheUserList;
        }

        String lockKey = RedisConstant.LOCK_PREFIX + redisKey;
        Supplier<List<UserDto>> supplier = () -> {
            // double check
            List<UserDto> userList = redisStringService.get(redisKey);
            if (Objects.nonNull(userList)) {
                return userList;
            }
            // 查询角色关联用户主键
            List<Long> userIdList = roleUserService.getUserIdList(roleKey);
            if (userIdList.isEmpty()) {
                log.info("角色键关联的用户主键为空");
                return Collections.emptyList();
            }
            // 调用user模块
            userList = yuUserResolver.getUserList(userIdList);

            // 添加缓存
            redisStringService.set(redisKey, userList, randomNum, TimeUnit.SECONDS);

            return userList;
        };
        // 加锁运行
        return redissonService.lockRun(lockKey, supplier);
    }

    @Override
    public void updateUser(UserVo userVo) {
        yuUserResolver.updateUser(userVo);
    }

    @Override
    public void deleteUser(List<Long> idList) {
        // 1、删除用户
        Integer count = yuUserResolver.deleteUser(idList);
        if (count != 0) {
            // 2、删除用户关联的角色
            roleUserService.deleteRoleUser(null, idList);
        }
    }
}
